import java.util.ArrayList;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author Sean Barkley, Matthew Metcalf, James Kuglics
 * 
 */
public class Banker {

	private int nUnits;
	private int numUnitsOnHand;
	private ConcurrentHashMap<Thread, int[]> threadMap = new ConcurrentHashMap<Thread, int[]>();

	public Banker(int nUnits) {
		this.nUnits = nUnits;
	}

	public void setClaim(int nUnits) {
		if (threadMap.containsKey(Thread.currentThread()) || nUnits <= 0
				|| nUnits > this.nUnits) {
			System.exit(1);
		}

		int[] allocationRemaining = new int[2];
		allocationRemaining[0] = 0;
		allocationRemaining[1] = nUnits;
		threadMap.put(Thread.currentThread(), allocationRemaining);

		System.out.println("Thread " + Thread.currentThread().getName()
				+ " sets a claim for " + nUnits + " units.");
	}

	// TODO
	public boolean request(int nUnits) {
		if (!threadMap.containsKey(Thread.currentThread()) || nUnits <= 0
				|| nUnits > threadMap.get(Thread.currentThread())[1]) {
			System.out.println("exit");
			System.exit(1);
		}
		
		System.out.println("Thread " + Thread.currentThread().getName()
					+ " requests " + nUnits + " units.");
		
		synchronized(this){
			int numUnitsOnHand = nUnits;
			ConcurrentHashMap<Thread, int[]> threadMapCopy = threadMap;
			boolean safeState = false;
			
			ArrayList<int[]> claimPairs = new ArrayList<int[]>();
			
			for (ConcurrentHashMap.Entry<Thread, int[]> entry : threadMapCopy.entrySet()){
				claimPairs.add(entry.getValue());
			}		
			
			for(int[] claim : claimPairs){
				if(claim[1] > numUnitsOnHand){
					safeState = false;
					break;
				}else{
					safeState = true;
					numUnitsOnHand += claim[1];
				}
			}
			
			if(safeState){
				System.out.println("Thread " + Thread.currentThread().getName() + 
						" has " + nUnits + " units allocated.");
				
				this.nUnits -= nUnits;
				int[] newAllocRemaining = new int[2];
				newAllocRemaining[0] = threadMap.get(Thread.currentThread())[0];
				newAllocRemaining[1] = threadMap.get(Thread.currentThread())[1];
				
				threadMap.replace(Thread.currentThread(), newAllocRemaining);
				return true;
			}
			
			return false;
		}
			
			/*boolean aquired = false;
			while (aquired == false) {
				if (this.nUnits >= nUnits) {
					this.nUnits -= nUnits;
					aquired = true;
					System.out.println("Thread "
							+ Thread.currentThread().getName() + " has "
							+ nUnits + " units allocated");
				} else {
					System.out.println("Thread "
							+ Thread.currentThread().getName() + " waits.");
					try {
						wait();
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
			}*/

		
	}

	// TODO
	public void release(int nUnits) {

	}

	public int allocated() {
		return nUnits;
	}

	public int remaining() {
		return threadMap.get(Thread.currentThread())[1];
	}

}
